package b.sync.domain

import b.sync.utils.Macros
import cats.effect._

import scala.language.experimental.macros

trait Replicator[R, W, K] extends DbSync {

  val stmtInsert: String
  val stmtUpdate: String
  val stmtDelete: String

  protected def macroReplicate[A, AK]: IO[Int] = macro Macros.replicate[A, AK]

  def replicate(rows: List[(R, Int, Int)]): IO[Int] = {
    val copies = rows.map { case (data, no, op) =>
      val copy = copyTo(data, op)
      (copy._1, no, copy._2)
    }
    val grouped = copies.foldLeft(List.empty[(Int, List[(W, Int)])]) { case (grouping, (data, no, op)) =>
      grouping.lastOption match {
        case Some((`op`, list)) => grouping.dropRight(1) :+ (op, list :+ (data, no))
        case _ => grouping :+ (op, List((data, no)))
      }
    }
    grouped.foldLeft(IO(0)) { (count, group) =>
      for {
        a <- count
        b <- replicateOne(group)
      } yield a + b
    }
  }

  def replicateOne(rowsGroup: (Int, List[(W, Int)])): IO[Int]

  def copyTo(row: R, opType: Int): (W, Int)

  def uniqueKeyOf(row: W): K
}
